
#include "PD_UFP_Log.h"
#include "PD_UFP_Protocol.h"
#include "FUSB302_UFP.h"
#include <stdint.h>
#include "string.h"
#include "stdio.h"
#include "PD_UFP.h"
#include "fusb302Port.h"
#define PSTR(x) (x)
extern PD_protocol_t protocol;
extern uint8_t status_initialized;
extern FUSB302_dev_t FUSB302;
extern uint16_t ready_voltage;
extern uint16_t ready_current;
extern status_power_t status_power;

/*
日志打印接口 整理代码自PD_Micro
*/
// status log object queue
uint32_t status_log_obj[16]; // array size must be power of 2 and <=256
uint8_t status_log_obj_read;
uint8_t status_log_obj_write;

uint8_t status_log_read;
uint8_t status_log_write;
status_log_t status_log[16]; // array size must be power of 2 and <=256

// state variables
pd_log_level_t status_log_level;
uint8_t status_log_counter;
char status_log_time[8];

#define STATUS_LOG_MASK (sizeof(status_log) / sizeof(status_log[0]) - 1)
#define STATUS_LOG_OBJ_MASK (sizeof(status_log_obj) / sizeof(status_log_obj[0]) - 1)

#define SNPRINTF snprintf
#define LOG(format, ...)                                   \
    do                                                     \
    {                                                      \
        n = SNPRINTF(buffer, maxlen, format, __VA_ARGS__); \
    } while (0)

void pu_log_init(pd_log_level_t log_level)
{
    status_log_write = 0;
    status_log_read = 0;
    status_log_counter = 0;
    status_log_obj_read = 0;
    status_log_obj_write = 0;
    status_log_level = log_level;
}

uint8_t pu_status_log_obj_add(uint16_t header, uint32_t *obj)
{
    if (obj)
    {
        uint8_t i, w = status_log_obj_write, r = status_log_obj_read;
        PD_msg_info_t info;
        PD_protocol_get_msg_info(header, &info);
        for (i = 0; i < info.num_of_obj && (uint8_t)(w - r) < STATUS_LOG_OBJ_MASK; i++)
        {
            status_log_obj[w++ & STATUS_LOG_OBJ_MASK] = obj[i];
        }
        status_log_obj_write = w;
        return i;
    }
    return 0;
}

void pu_status_log_event(uint8_t status, uint32_t *obj)
{
    if (((status_log_write - status_log_read) & STATUS_LOG_MASK) >= STATUS_LOG_MASK)
    {
        return;
    }
    status_log_t *log = &status_log[status_log_write & STATUS_LOG_MASK];
    switch (status)
    {
    case STATUS_LOG_MSG_TX:
        log->msg_header = PD_protocol_get_tx_msg_header(&protocol);
        log->obj_count = pu_status_log_obj_add(log->msg_header, obj);
        break;
    case STATUS_LOG_MSG_RX:
        log->msg_header = PD_protocol_get_rx_msg_header(&protocol);
        log->obj_count = pu_status_log_obj_add(log->msg_header, obj);
        break;
    default:
        break;
    }
    log->status = status;
    log->time = puClockMs();
    status_log_write++;
}

int pu_status_log_readline_msg(char *buffer, int maxlen, status_log_t *log)
{
    char *t = status_log_time;
    int n = 0;
    if (status_log_counter == 0)
    {
        // output message header
        char type = log->status == STATUS_LOG_MSG_TX ? 'T' : 'R';
        PD_msg_info_t info;
        PD_protocol_get_msg_info(log->msg_header, &info);
        if (status_log_level >= PD_LOG_LEVEL_VERBOSE)
        {
            const char *ext = info.extended ? "ext, " : "";
            LOG("%s%cX %s id=%d %sraw=0x%04X\n", t, type, info.name, info.id, ext, log->msg_header);
            if (info.num_of_obj)
            {
                status_log_counter++;
            }
        }
        else
        {
            LOG("%s%cX %s\n", t, type, info.name);
        }
    }
    else
    {
        // output object data
        int i = status_log_counter - 1;
        uint32_t obj = status_log_obj[status_log_obj_read++ & STATUS_LOG_OBJ_MASK];
        LOG("%s obj%d=0x%08lX\n", t, i, obj);
        if (++status_log_counter > log->obj_count)
        {
            status_log_counter = 0;
        }
    }
    return n;
}

int pu_status_log_readline_src_cap(char *buffer, int maxlen)
{
    PD_power_info_t p;
    int n = 0;
    uint8_t i = status_log_counter;
    if (PD_protocol_get_power_info(&protocol, i, &p))
    {
        const char *str_pps[] = {"", " BAT", " VAR", " PPS"}; /* PD_power_data_obj_type_t */
        char *t = status_log_time;
        uint8_t selected = PD_protocol_get_selected_power(&protocol);
        char min_v[8] = {0}, max_v[8] = {0}, power[8] = {0};
        if (p.min_v)
            SNPRINTF(min_v, sizeof(min_v) - 1, PSTR("%d.%02dV-"), p.min_v / 20, (p.min_v * 5) % 100);
        if (p.max_v)
            SNPRINTF(max_v, sizeof(max_v) - 1, PSTR("%d.%02dV"), p.max_v / 20, (p.max_v * 5) % 100);
        if (p.max_i)
        {
            SNPRINTF(power, sizeof(power) - 1, PSTR("%d.%02dA"), p.max_i / 100, p.max_i % 100);
        }
        else
        {
            SNPRINTF(power, sizeof(power) - 1, PSTR("%d.%02dW"), p.max_p / 4, p.max_p * 25);
        }
        LOG("%s   [%d] %s%s %s%s%s\n", t, i, min_v, max_v, power, str_pps[p.type], i == selected ? " *" : "");
        status_log_counter++;
    }
    else
    {
        status_log_counter = 0;
    }
    return n;
}

int pu_status_log_readline(char *buffer, int maxlen)
{
    if (status_log_write == status_log_read)
    {
        return 0;
    }

    status_log_t *log = &status_log[status_log_read & STATUS_LOG_MASK];
    int n = 0;
    char *t = status_log_time;
    if (t[0] == 0)
    { // Convert timestamp number to string
        SNPRINTF(t, sizeof(status_log_time) - 1, PSTR("%04u: "), log->time);
        return 0;
    }

    switch (log->status)
    {
    case STATUS_LOG_MSG_TX:
    case STATUS_LOG_MSG_RX:
        n = pu_status_log_readline_msg(buffer, maxlen, log);
        break;
    case STATUS_LOG_DEV:
        if (status_initialized)
        {
            uint8_t version_ID = 0, revision_ID = 0;
            FUSB302_get_ID(&FUSB302, &version_ID, &revision_ID);
            LOG("\n%sFUSB302 ver ID:%c_rev%c\n", t, 'A' + version_ID, 'A' + revision_ID);
        }
        else
        {
            LOG("\n%sFUSB302 init error\n", t);
        }
        break;
    case STATUS_LOG_CC:
    {
        const char *detection_type_str[] = {"USB", "1.5", "3.0"};
        uint8_t cc1 = 0, cc2 = 0;
        FUSB302_get_cc(&FUSB302, &cc1, &cc2);
        if (cc1 == 0 && cc2 == 0)
        {
            LOG("%sUSB attached vRA\n", t);
        }
        else if (cc1 && cc2 == 0)
        {
            LOG("%sUSB attached CC1 vRd-%s\n", t, detection_type_str[cc1 - 1]);
        }
        else if (cc2 && cc1 == 0)
        {
            LOG("%sUSB attached CC2 vRd-%s\n", t, detection_type_str[cc2 - 1]);
        }
        else
        {
            LOG("%sUSB attached unknown\n", t);
        }
        break;
    }
    case STATUS_LOG_SRC_CAP:
        n = pu_status_log_readline_src_cap(buffer, maxlen);
        break;
    case STATUS_LOG_POWER_READY:
    {
        uint16_t v = ready_voltage;
        uint16_t a = ready_current;
        if (status_power == STATUS_POWER_TYP)
        {
            LOG("%s%d.%02dV %d.%02dA supply ready\n", t, v / 20, (v * 5) % 100, a / 100, a % 100);
        }
        else if (status_power == STATUS_POWER_PPS)
        {
            LOG("%sPPS %d.%02dV %d.%02dA supply ready\n", t, v / 50, (v * 2) % 100, a / 20, (a * 5) % 100);
        }
        break;
    }
    case STATUS_LOG_POWER_PPS_STARTUP:
        LOG("%sPPS 2-stage startup\n", t);
        break;
    case STATUS_LOG_POWER_REJECT:
        LOG("%sRequest Rejected\n", t);
        break;
    case STATUS_LOG_LOAD_SW_ON:
        LOG("%sLoad SW ON\n", t);
        break;
    case STATUS_LOG_LOAD_SW_OFF:
        LOG("%sLoad SW OFF\n", t);
        break;
    }
    if (status_log_counter == 0)
    {
        t[0] = 0;
        status_log_read++;
        status_log_counter = 0;
    }
    return n;
}
void pu_print_status(void)
{
    static uint8_t lineEnd = 0;
    uint16_t ret = 0;
    char buf[32];
    ret = pu_status_log_readline(buf, 31);
    if (ret != 0)
    {
        lineEnd = 0;
        puStdPrint(buf);
    }
    else
    {
        if (lineEnd == 0)
        {
            lineEnd = 1;
            puStdPrint("\n");
        }
    }
}
